var Projectile = require('js/turrets/projectile').Projectile;
var Turret = require('js/turret').Turret;
var Enemy = require('js/enemy').Enemy;
var Match = require('js/match').Match;
var Map = require('js/map').Map;
var gamejs = require('gamejs');

qModule('js/projectile', {
	setup: function() {
		this.match = new Match();
		this.map = new Map(this.match, 800, 400);
		this.match.setMap(this.map);
		this.turret = new Turret(this.match, [100, 100]);
		this.enemy = new Enemy(this.match, [700, 100], 0);
		this.projectile = new Projectile(this.enemy, this.turret);
	}
});

test("Projectile(target, fired_by, power, speed)", function() {
	// Test on a projectile created with defaults argument
	ok(this.projectile instanceof Projectile,
		"Test with arguments (Enemy, Turret, default, default): " +
		"object correctly created"
	);
	deepEqual(this.projectile.getTarget(), this.enemy,
		"Test with arguments (Enemy, Turret, default, default): " +
		"target correctly set"
	);
	deepEqual(this.projectile.getSource(), this.turret,
		"Test with arguments (Enemy, Turret, default, default): " +
		"fired_by correctly set"
	);
	deepEqual(this.projectile.getSpeed(), Projectile.DEFAULT_SPEED,
		"Test with arguments (Enemy, Turret, default, default): " +
		"default speed correctly set"
	);
	deepEqual(this.projectile.getPower(), Projectile.DEFAULT_POWER,
		"Test with arguments (Enemy, Turret, default, default): " +
		"default power correctly set"
	);
	// Test on projectile with custom speed and power
	var custom_speed = 10;
	var custom_power = 10000;
	var customPj = new Projectile(this.enemy, this.turret,
		custom_power, custom_speed
	);
	ok(customPj instanceof Projectile,
		"Test with arguments (Enemy, Turret, Number, Number): " +
		"object correctly created"
	);
	equal(customPj.getSpeed(), custom_speed,
		"Test with arguments (Enemy, Turret, Number, Number): " +
		"speed correctly set"
	);
	equal(customPj.getPower(), custom_power,
		"Test with arguments (Enemy, Turret, Number, Number): " +
		"power correctly set"
	);
	// Test that exceptions are correctly throwed
	throws(
		// undefined is not targettable and hittable
		function() { new Projectile(undefined, this.turret); },
		TypeError,
		"Test with arguments (undefined, Turret, defaul, default): " +
		"exception correctly throwed"
	);
	throws(
		// undefined doesn't have getWeaponCoordinates() method
		function() { new Projectile(this.enemy, undefined); },
		TypeError,
		"Test with arguments (Enemy, undefined, defaul, default): " +
		"exception correctly throwed"
	);
	throws(
		function() { new Projectile(this.enemy, this.turret, NaN, 100); },
		TypeError,
		"Test with arguments (Enemy, Turret, NaN, default): " +
		"exception correctly throwed"
	);
	throws(
		function() { new Projectile(this.enemy, this.turret, 100, NaN); },
		TypeError,
		"Test with arguments (Enemy, Turret, default, NaN): " +
		"exception correctly throwed"
	);
	throws(
		function() { new Projectile(this.enemy, this.turret, 0, 100); },
		RangeError,
		"Test with arguments (Enemy, Turret, Number=0, Number): " +
		"exception correctly throwed"
	);
	throws(
		function() { new Projectile(this.enemy, this.turret, -1, 100); },
		RangeError,
		"Test with arguments (Enemy, Turret, Number<0, Number): " +
		"exception correctly throwed"
	);
	throws(
		function() { new Projectile(this.enemy, this.turret, 100, 0); },
		RangeError,
		"Test with arguments (Enemy, Turret, Number, Number=0): " +
		"exception correctly throwed"
	);
	throws(
		function() { new Projectile(this.enemy, this.turret, 100, -1); },
		RangeError,
		"Test with arguments (Enemy, Turret, Number, Number<0): " +
		"exception correctly throwed"
	);
});

test("Projectile.getTarget()", function() {
	// Test with setup projectile
	deepEqual(this.projectile.getTarget(), this.enemy, "Works!");
});

test("Projectile.setTarget(new_target)", function() {
	// Test with an object hittable and targettable
	var newTarget = new Enemy(this.match, [600,300], 0);
	this.projectile.setTarget(newTarget);
	deepEqual(this.projectile.getTarget(), newTarget,
		"Test with argument (Enemy): new target correctly set"
	);
	// Test with an object that not targettable and not hittable
	throws(
		function() { this.projectile.setTarget(undefined); },
		TypeError,
		"Test with argument (undefined): exception correctly throwed"
	);
});

test("Projectile.getSource()", function() {
	// Test with setup projectile
	deepEqual(this.projectile.getSource(), this.turret, "Works!");
});

test("Projectile.getSpeed()", function() {
	// Test with setup projectile
	deepEqual(this.projectile.getSpeed(), Projectile.DEFAULT_SPEED, "Works!");
});

test("Projectile.getPower()", function() {
	// Test with setup projectile
	deepEqual(this.projectile.getPower(), Projectile.DEFAULT_POWER, "Works!");
});

test("Projectile._calculateAngle()", function() {
	// A mock turret with its weapon centerd in [400, 200]
	// (useful to make calculation easier)
	var mockTurret = {
		getWeaponCoordinates: function() { return [400,200]; }
	};
	// This target will use a 1-pixel rect to have rect.topleft == rect.center
	// (useful to make calculation easier)
	var mockTarget = {
		hit: function(hitrate) {return;}
	};
	var projectile;
	// 0
	mockTarget.rect = new gamejs.Rect([500,200]);
	projectile = new Projectile(mockTarget, mockTurret);
	equal(projectile._calculateAngle(), 0,
		"Works with Turret@[400, 200] targetting a Target@[500,200]"
	);
	// Math.PI/4
	mockTarget.rect = new gamejs.Rect([500,300]);
	equal(projectile._calculateAngle(), Math.PI/4,
		"Works with Turret@[400, 200] targetting a Target@[500,300]"
	);
	// Math.PI/2
	mockTarget.rect = new gamejs.Rect([400,300]);
	equal(projectile._calculateAngle(), Math.PI/2,
		"Works with Turret@[400, 200] targetting a Target@[400,300]"
	);
	// 3*Math.PI/4
	mockTarget.rect = new gamejs.Rect([300,300]);
	equal(projectile._calculateAngle(), 3*Math.PI/4,
		"Works with Turret@[400, 200] targetting a Target@[300,300]"
	);
	// Math.PI
	mockTarget.rect = new gamejs.Rect([300,200]);
	equal(projectile._calculateAngle(), Math.PI,
		"Works with Turret@[400, 200] targetting a Target@[300,200]"
	);
	// -3*Math.PI/4
	mockTarget.rect = new gamejs.Rect([300,100]);
	equal(projectile._calculateAngle(), -3*Math.PI/4,
		"Works with Turret@[400, 200] targetting a Target@[300,100]"
	);
	// -Math.PI/2
	mockTarget.rect = new gamejs.Rect([400,100]);
	equal(projectile._calculateAngle(), -Math.PI/2,
		"Works with Turret@[400, 200] targetting a Target@[400,100]"
	);
	// -Math.PI/2
	mockTarget.rect = new gamejs.Rect([500,100]);
	equal(projectile._calculateAngle(), -Math.PI/4,
		"Works with Turret@[400, 200] targetting a Target@[500,100]"
	);
});

